/*******************************************************************************
 * Copyright 2011 Andrea Guitto
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.andreaguitto.mobile.android.framework.base.orm;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;

import android.content.ContentResolver;
import android.database.CharArrayBuffer;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Bundle;

import com.andreaguitto.mobile.android.framework.base.annotations.AGColumn;
import com.andreaguitto.mobile.android.framework.base.annotations.AGColumnId;

public class AGSQLiteCursor<T extends AGSQLiteDatabaseObject> implements Cursor {

	private static final String setMethodPrefix ="set";
	private static final String getMethodPrefixSTD ="get";
	private static final String getMethodPrefixBOOL ="is";
	
	Class<T> objectClass;
	
	Cursor cursor;
	public Cursor getCursor(){	return cursor;	}
	Collection<T> list;
	
	@SuppressWarnings("rawtypes")
	public static AGSQLiteCursor getInstance(Class objectClass, Cursor cursor){
		return new AGSQLiteCursor(objectClass, cursor);
	} 

	@SuppressWarnings("unchecked")
	public AGSQLiteCursor(@SuppressWarnings("rawtypes") Class objectClass, Cursor cursor){
		this.cursor= cursor;
		this.objectClass = objectClass;
	}
    /**
     * Get collection from cursor. Will not autoclose cursor.
     * 
     * @param select
     * @return
     */
    public Collection<T> getCollection() 
    {
    	if(list==null){
	    	if (this.getCount()>0){
	    		list = new ArrayList<T>();
	    		
	    		this.moveToFirst();
	    		
	    		try {
					list.add(getObject());
				} catch (Exception e) {
					e.printStackTrace();
					if(list!=null) 
						list.clear();
					list = null;
				}
	
				if(list!=null){
		    		while (this.moveToNext()){
		    			try{
		    				list.add(getObject());
		    			}catch(Exception e){
		    				e.printStackTrace();
		    				if(list!=null)
		    					list.clear();
		    				list=null;
		    			}
		    		}
				}
	    	}
    	}
    	
    	return list;
    }     	

    /**
     * Get object from current position of cursor.
     * @param autoclose close cursor after query and retrieve collection.
     * @return
     */ 
    public Collection<T> getCollection(boolean autoclose){
    	try{
    		list = getCollection();
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    	
    	if(autoclose){
    		if(!cursor.isClosed())
    			cursor.close();
    	}
    	
    	return list;
    }    
    
    /**
     * Get object from current position of cursor. Will not autoclose cursor.
     * 
     * @param select
     * @return
     */        
	public final T getObject() throws Exception {
		
		T object = this.objectClass.newInstance();
		
		boolean isGetMethod;
		boolean isBoolMethod;
		
		for(Method getMethod : object.getClass().getDeclaredMethods()){
			
			isGetMethod = false;
			isBoolMethod = false;

			isBoolMethod= getMethod.getName().startsWith(getMethodPrefixBOOL);
			isGetMethod = getMethod.getName().startsWith(getMethodPrefixSTD) || isBoolMethod;

			
			if(isGetMethod){
				String name = null;
				
				if(isBoolMethod)
					name = getMethod.getName().substring(2, getMethod.getName().length());
				else
					name = getMethod.getName().substring(3, getMethod.getName().length());
				
				if(name!=null){
					Annotation[] annotations = getMethod.getDeclaredAnnotations();
			
					for(Annotation annotation : annotations){
					    if(annotation instanceof AGColumn){
					    	AGColumn myAnnotation = (AGColumn) annotation;
					        
					    	int columnIndex = this.getColumnIndex(myAnnotation.name());
					        @SuppressWarnings("rawtypes")
							Class returnType = getMethod.getReturnType();
					        
					        Method setMethod = object.getClass().getMethod(setMethodPrefix+name, returnType);
					        
					        if(returnType.equals(int.class))
					        	setMethod.invoke(object, this.getInt(columnIndex));
					        else if(returnType.equals(long.class))
					        	setMethod.invoke(object, this.getLong(columnIndex));
					        else if(returnType.equals(String.class))
					        	setMethod.invoke(object, this.getString(columnIndex));
					        else if(returnType.equals(short.class))
					        	setMethod.invoke(object, this.getShort(columnIndex));
					        else if(returnType.equals(double.class))
					        	setMethod.invoke(object, this.getDouble(columnIndex));
					        else if(returnType.equals(float.class))
					        	setMethod.invoke(object, this.getFloat(columnIndex));
					        
					    }else if(annotation instanceof AGColumnId){
					    	AGColumnId myAnnotation = (AGColumnId) annotation;
					        
					    	int columnIndex = this.getColumnIndex(myAnnotation.name());
					        @SuppressWarnings("rawtypes")
							Class returnType = getMethod.getReturnType();
					        
					        Method setMethod = object.getClass().getMethod(setMethodPrefix+name, returnType);
					        
					        if(returnType.equals(int.class))
					        	setMethod.invoke(object, this.getInt(columnIndex));
					        else if(returnType.equals(long.class))
					        	setMethod.invoke(object, this.getLong(columnIndex));
					        else if(returnType.equals(String.class))
					        	setMethod.invoke(object, this.getString(columnIndex));
					        else if(returnType.equals(short.class))
					        	setMethod.invoke(object, this.getShort(columnIndex));
					        else if(returnType.equals(double.class))
					        	setMethod.invoke(object, this.getDouble(columnIndex));
					        else if(returnType.equals(float.class))
					        	setMethod.invoke(object, this.getFloat(columnIndex));
					    }
					}
					
					annotations = null;
				}				
			}
		}
		return object;
	}

	@Override
	public void close() {
		list = null;
		cursor.close();
	}

	@Override
	public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
		cursor.copyStringToBuffer(columnIndex, buffer);
	}

	@Override
	public void deactivate() {
		cursor.deactivate();
		list=null;
	}

	@Override
	public byte[] getBlob(int columnIndex) {
		return cursor.getBlob(columnIndex);
	}

	@Override
	public int getColumnCount() {
		return cursor.getColumnCount();
	}

	@Override
	public int getColumnIndex(String columnName) {
		return cursor.getColumnIndex(columnName);
	}

	@Override
	public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
		return cursor.getColumnIndexOrThrow(columnName);
	}

	@Override
	public String getColumnName(int columnIndex) {
		return cursor.getColumnName(columnIndex);
	}

	@Override
	public String[] getColumnNames() {
		return cursor.getColumnNames();
	}

	@Override
	public int getCount() {
		return cursor.getCount();
	}

	@Override
	public double getDouble(int columnIndex) {
		return cursor.getDouble(columnIndex);
	}

	@Override
	public Bundle getExtras() {
		return cursor.getExtras();
	}

	@Override
	public float getFloat(int columnIndex) {
		return cursor.getFloat(columnIndex);
	}

	@Override
	public int getInt(int columnIndex) {
		// TODO Auto-generated method stub
		return cursor.getInt(columnIndex);
	}

	@Override
	public long getLong(int columnIndex) {
		// TODO Auto-generated method stub
		return cursor.getLong(columnIndex);
	}

	@Override
	public int getPosition() {
		// TODO Auto-generated method stub
		return cursor.getPosition();
	}

	@Override
	public short getShort(int columnIndex) {
		// TODO Auto-generated method stub
		return cursor.getShort(columnIndex);
	}

	@Override
	public String getString(int columnIndex) {
		// TODO Auto-generated method stub
		return cursor.getString(columnIndex);
	}

	@Override
	public boolean getWantsAllOnMoveCalls() {
		// TODO Auto-generated method stub
		return cursor.getWantsAllOnMoveCalls();
	}

	@Override
	public boolean isAfterLast() {
		// TODO Auto-generated method stub
		return cursor.isAfterLast();
	}

	@Override
	public boolean isBeforeFirst() {
		// TODO Auto-generated method stub
		return cursor.isBeforeFirst();
	}

	@Override
	public boolean isClosed() {
		// TODO Auto-generated method stub
		return cursor.isClosed();
	}

	@Override
	public boolean isFirst() {
		// TODO Auto-generated method stub
		return cursor.isFirst();
	}

	@Override
	public boolean isLast() {
		// TODO Auto-generated method stub
		return cursor.isLast();
	}

	@Override
	public boolean isNull(int columnIndex) {
		// TODO Auto-generated method stub
		return cursor.isNull(columnIndex);
	}

	@Override
	public boolean move(int offset) {
		// TODO Auto-generated method stub
		return cursor.move(offset);
	}

	@Override
	public boolean moveToFirst() {
		// TODO Auto-generated method stub
		return cursor.moveToFirst();
	}

	@Override
	public boolean moveToLast() {
		// TODO Auto-generated method stub
		return cursor.moveToLast();
	}

	@Override
	public boolean moveToNext() {
		// TODO Auto-generated method stub
		return cursor.moveToNext();
	}

	@Override
	public boolean moveToPosition(int position) {
		// TODO Auto-generated method stub
		return cursor.moveToPosition(position);
	}

	@Override
	public boolean moveToPrevious() {
		// TODO Auto-generated method stub
		return cursor.moveToPrevious();
	}

	@Override
	public void registerContentObserver(ContentObserver observer) {
		// TODO Auto-generated method stub
		cursor.registerContentObserver(observer);
	}

	@Override
	public void registerDataSetObserver(DataSetObserver observer) {
		// TODO Auto-generated method stub
		cursor.registerDataSetObserver(observer);
	}

	@Override
	public boolean requery() {
		// TODO Auto-generated method stub
		list = null;
		return cursor.requery();
	}

	@Override
	public Bundle respond(Bundle extras) {
		// TODO Auto-generated method stub
		return cursor.respond(extras);
	}

	@Override
	public void setNotificationUri(ContentResolver cr, Uri uri) {
		// TODO Auto-generated method stub
		cursor.setNotificationUri(cr, uri);
	}

	@Override
	public void unregisterContentObserver(ContentObserver observer) {
		// TODO Auto-generated method stub
		cursor.unregisterContentObserver(observer);
	}

	@Override
	public void unregisterDataSetObserver(DataSetObserver observer) {
		// TODO Auto-generated method stub
		cursor.unregisterDataSetObserver(observer);
	}    
	

}
